/*===========================================
	Paradigm Engine for Twilight Hack
	Build 1.0
	
	(C)2008 Vicious1988 - Syndicate Development
	Based off simple rotating cube demo by tkcne
===========================================*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <math.h>
#include <ogcsys.h>
#include <gccore.h>
#include "c_primitives.h"

// c_primitives
extern int totalcubes,totalspheres,totalpolys;
extern int create_cube(int dscale);

//scene
extern void drawScene(Mtx v);

//textures
extern int LoadTexture(const char *filename);

//modifiers
extern void setPosition(int target,float x1, float y1, float z1);
extern void setRelativePosition (int target, int relative, float x1, float y1, float z1);
extern void setRotation(int target,float x1, float y1, float z1);
extern void setScale(int target,float x1, float y1, float z1);
extern void setTexture (int target,int texname);
extern void isTextured (int target,int textured);

//cameras
extern Vector cam;
extern Vector up;
extern Vector look;
extern int camera_create ();
extern void camera_setActive();
extern void camera_setPosition();
extern void camera_move();
extern void camera_update();
extern void camera_setRotation();
extern void camera_look();
extern void camera_strafe();


//local
void draw_init ();
static void copy_to_xfb (u32 count);

#define DEFAULT_FIFO_SIZE	(256*1024)

// color data
u8 colors[] ATTRIBUTE_ALIGN (32) =
{
  // r, g, b, a
  100, 10, 100, 255,		// 0 purple
    240, 0, 0, 255,		// 1 red
    255, 180, 0, 255,		// 2 orange
    255, 255, 0, 255,		// 3 yellow
    10, 120, 40, 255,		// 4 green
    0, 20, 100, 255		// 5 blue
};

static u32* xfb[2] = { NULL, NULL };
static u32 do_copy = GX_FALSE;
//GXRModeObj *vmode;
GXRModeObj *vmode;	
int whichfb=0;

int
main ()
{
  Mtx v, p;			// view and perspective matrices
  GXColor background = { 0, 0, 0, 0xff };

  // init the vi. setup frame buffer and set the retrace callback
  // to copy the efb to xfb
  VIDEO_Init ();

  switch (VIDEO_GetCurrentTvMode ())
    {
    case VI_NTSC:
      vmode = &TVNtsc480IntDf;
      break;
    case VI_PAL:
      vmode = &TVPal528IntDf;
      break;
    case VI_MPAL:
      vmode = &TVMpal480IntDf;
      break;
    default:
      vmode = &TVNtsc480IntDf;
      break;
    }


  PAD_Init ();
  VIDEO_Configure (vmode);
  xfb[0] = MEM_K0_TO_K1 (SYS_AllocateFramebuffer (vmode));
  xfb[1] = MEM_K0_TO_K1 (SYS_AllocateFramebuffer (vmode));
  //console_init(xfb[0],20,20,vmode->fbWidth,vmode->xfbHeight,vmode->fbWidth*VI_DISPLAY_PIX_SZ);
  //printf ("\n"); 
  VIDEO_ClearFrameBuffer (vmode, xfb[0], COLOR_BLACK);
  VIDEO_ClearFrameBuffer (vmode, xfb[1], COLOR_BLACK);
  VIDEO_SetNextFramebuffer (xfb[0]);
  VIDEO_SetPostRetraceCallback (copy_to_xfb);
  VIDEO_SetBlack (FALSE);
  VIDEO_Flush ();
  //VIDEO_WaitVSync();
  //if(vmode->viTVMode&VI_NON_INTERLACE) VIDEO_WaitVSync();

  // setup the fifo and then init the flipper
  void *gp_fifo = NULL;
  gp_fifo = MEM_K0_TO_K1 (memalign (32, DEFAULT_FIFO_SIZE));
  memset (gp_fifo, 0, DEFAULT_FIFO_SIZE);

 // initiate GX subsystem
 GX_Init (gp_fifo, DEFAULT_FIFO_SIZE);

  // clears the bg to color and clears the z buffer
  GX_SetCopyClear (background, 0x00ffffff);

  // other gx setup
  GX_SetViewport (0, 0, vmode->fbWidth, vmode->efbHeight, 0, 1);
  GX_SetDispCopyYScale ((f32) vmode->xfbHeight / (f32) vmode->efbHeight);
  GX_SetScissor (0, 0, vmode->fbWidth, vmode->efbHeight);
  GX_SetDispCopySrc (0, 0, vmode->fbWidth, vmode->efbHeight);
  GX_SetDispCopyDst (vmode->fbWidth, vmode->xfbHeight);
  GX_SetCopyFilter (vmode->aa, vmode->sample_pattern, GX_TRUE,
		    vmode->vfilter);
  GX_SetFieldMode (vmode->field_rendering,
		   ((vmode->viHeight ==
		     2 * vmode->xfbHeight) ? GX_ENABLE : GX_DISABLE));

  // cull none because other values produce weird results
  GX_SetCullMode (GX_CULL_NONE);
  GX_CopyDisp (xfb[0], GX_TRUE);
  GX_CopyDisp (xfb[0], GX_TRUE);
  GX_SetDispCopyGamma (GX_GM_1_0);

  // setup our projection matrix
  f32 w = vmode->viWidth;
  f32 h = vmode->viHeight;
  guPerspective (p, 60, (f32) w / h, 10.0F, 300.0F);
  GX_LoadProjectionMtx (p, GX_PERSPECTIVE);

  // initiate GX arrays and matrices
  draw_init ();
  
  ////////////////////////////////////////////////////////////////
  //                                                           //
  //       Everything until draw_init  is safe to edit        //
  //                                                         //
  ////////////////////////////////////////////////////////////
  
  // initiate camera
	int cam1=camera_create();
  
  // initiate resources (primitives, models, textures, etc.)
	int cube1=create_cube(1); // define our cube
	int cube2=create_cube(1);
	int cube3=create_cube(1);
	int cube4=create_cube(1);
	int cube5=create_cube(1);
	int cube6=create_cube(1);
	int texture1=LoadTexture("paradigm/test.png");// load our texture
	int texture2=LoadTexture("paradigm/syndicate.png");// load our texture
	
	// cube 6 rotations
	float rotx1=0;
	float roty1=0;
	float rotz1=0;
	
	// camera rotations
	float rotxc=0;
	float rotyc=180;
	float rotzc=0;

  // main loop
  while (1)
    {
	  // update camera
	  camera_update(v);
      // do this before drawing
      GX_SetViewport (0, 0, vmode->fbWidth, vmode->efbHeight, 0, 1);
      GX_InvVtxCache ();
      GX_InvalidateTexAll ();
	  
	  rotx1++;
	  roty1++;
	  rotz1++;

      // change properties of cube1
	  setPosition(cube1,0,-30,0);
	  setScale(cube1,300,4,300);
	  isTextured(cube1,1);
	  setTexture(cube1,texture1);
	  
	  // change properties of cube2
	  setPosition(cube2,-150,20,0);
	  setScale(cube2,4,100,300);
	  isTextured(cube2,1);
	  setTexture(cube2,texture1);
	  
	  // change properties of cube3
	  setPosition(cube3,0,20,150);
	  setScale(cube3,300,100,4);
	  isTextured(cube3,1);
	  setTexture(cube3,texture1);
	  
	  // change properties of cube4
	  setPosition(cube4,0,20,-150);
	  setScale(cube4,300,100,4);
	  isTextured(cube4,1);
	  setTexture(cube4,texture1);
	  
	  // change properties of cube5
	  setPosition(cube5,150,20,0);
	  setScale(cube5,4,100,300);
	  isTextured(cube5,1);
	  setTexture(cube5,texture1);
	  
	   // change properties of cube6
	  setPosition(cube6,0,20,100);
	  setRotation(cube6,rotx1,roty1,rotz1);
	  setScale(cube6,20,20,20);
	  isTextured(cube6,1);
	  setTexture(cube2,texture2);
	  
  
	  // Draw the scene
	  drawScene(v);

      // do this stuff after drawing
      do_copy = GX_TRUE;
      VIDEO_WaitVSync ();
	  	
	  PAD_ScanPads();
      // start button resets to psoload
      if (PAD_ButtonsDown(0) & PAD_BUTTON_START)
		{
			void (*reload) () = (void (*)()) 0x80001800;
			reload ();
		}

	  // move around
	  if ((PAD_StickY (PAD_CHAN0)>5)||(PAD_StickY (PAD_CHAN0)<-5))
	    {
		    camera_move(cam1,fabs(PAD_StickY(PAD_CHAN0))/PAD_StickY(PAD_CHAN0));
		}
	  if ((PAD_StickX (PAD_CHAN0)>5)||(PAD_StickX (PAD_CHAN0)<-5))
	    {
		    camera_strafe(cam1,fabs(PAD_StickX(PAD_CHAN0))/PAD_StickX(PAD_CHAN0));
		}
		
	  // look around
	  if ((PAD_SubStickY (PAD_CHAN0)>5)||(PAD_SubStickY (PAD_CHAN0)<-5))
	    {
		    rotxc+=fabs(PAD_SubStickY(PAD_CHAN0))/PAD_SubStickY(PAD_CHAN0);
			//slightly bugged
			
		}
	  if ((PAD_SubStickX (PAD_CHAN0)>5)||(PAD_SubStickX (PAD_CHAN0)<-5))
	    {
		   rotyc-=fabs(PAD_SubStickX(PAD_CHAN0))/PAD_SubStickX(PAD_CHAN0);
		}
		camera_setRotation(cam1,rotxc,rotyc,rotzc);
    }
  return 0;
}


void
draw_init ()
{
  // setup the vertex descriptor
  GX_ClearVtxDesc ();
  GX_SetVtxDesc (GX_VA_POS, GX_INDEX8);
  GX_SetVtxDesc (GX_VA_CLR0, GX_INDEX8);
  GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT);

  // setup the vertex attribute table
  GX_SetVtxAttrFmt (GX_VTXFMT0, GX_VA_POS, GX_POS_XYZ, GX_S16, 0);
  GX_SetVtxAttrFmt (GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0);
  GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_F32, 0);
  
  GX_SetArray(GX_VA_CLR0, colors, 4*sizeof(u8));

  // no idea...sets to no textures
  // i don't know anything about textures or lighting yet :|
  	GX_SetNumTexGens(1);
	GX_SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY);
	
	GX_InvalidateTexAll();
}


// draws a quad from 4 vertex idx and one color idx
void
draw_quad (u8 v0, u8 v1, u8 v2, u8 v3, u8 c, int textured)
{
  GX_Position1x8 (v0);
  GX_Color1x8 (c);
  if (textured) GX_TexCoord2f32(0.0, 0.0);
  GX_Position1x8 (v1);
  GX_Color1x8 (c);
  if (textured) GX_TexCoord2f32(1.0, 0.0);
  GX_Position1x8 (v2);
  GX_Color1x8 (c);
  if (textured) GX_TexCoord2f32(1.0, 1.0);
  GX_Position1x8 (v3);
  GX_Color1x8 (c);
  if (textured) GX_TexCoord2f32(0.0, 1.0);
}

// copy efb to xfb when ready
static void
copy_to_xfb (u32 count)
{
  if (do_copy == GX_TRUE)
    {
	  whichfb ^= 1; // maybe delete
      GX_SetZMode (GX_TRUE, GX_LEQUAL, GX_TRUE);
      GX_SetColorUpdate (GX_TRUE);
      GX_CopyDisp (xfb[whichfb], GX_TRUE);
      GX_Flush ();
      do_copy = GX_FALSE;
	  VIDEO_SetNextFramebuffer (xfb[whichfb]); // maybe delete
    }
}
